/*
 * Copyright 2009 Jesse McLaughlin (nzjess@gmail.com)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.travelfusion.xmlclient.impl.handler;

import org.travelfusion.xmlclient.handler.HandlerPipeline;
import org.travelfusion.xmlclient.handler.HandlerPipelineFactory;
import org.travelfusion.xmlclient.handler.HandlerPipelineTypeLookup;
import org.travelfusion.xmlclient.handler.RequestHandler;
import org.travelfusion.xmlclient.handler.RequestHandlerInterceptor;
import org.travelfusion.xmlclient.handler.ResponseHandler;
import org.travelfusion.xmlclient.handler.ResponseHandlerInterceptor;
import org.travelfusion.xmlclient.handler.SeperateInstancesHandleRequestAndResponse;
import org.travelfusion.xmlclient.xobject.XRequest;
import org.travelfusion.xmlclient.xobject.XResponse;

import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;

/**
 * @author Jesse McLaughlin (nzjess@gmail.com)
 */
@Singleton
public class HandlerPipelineFactoryImpl implements HandlerPipelineFactory {

  private HandlerPipelineTypeLookup typeLookup;

  private Injector injector;

  @Inject
  public void setTypeLookup(HandlerPipelineTypeLookup typeLookup) {
    this.typeLookup = typeLookup;
  }

  @Inject
  public void setInjector(Injector injector) {
    this.injector = injector;
  }

  @SuppressWarnings("unchecked")
  public HandlerPipeline createHandlerPipeline(XRequest request) {
    try {
      final Class<? extends XRequest> requestType = request.getClass();

      Class<? extends RequestHandler<XRequest>> requestHandlerType = typeLookup.getRequestHandlerType(requestType);
      RequestHandler<XRequest> requestHandler = injector.getInstance(requestHandlerType);
      requestHandler.setRequest(request);

      Class<? extends RequestHandlerInterceptor> requestHandlerInterceptorType =
          typeLookup.getRequestHandlerInterceptorType((Class<? extends RequestHandler<XRequest>>)requestHandler.getClass());
      RequestHandlerInterceptor requestHandlerInterceptor = null;
      if (requestHandlerInterceptorType != null) {
        requestHandlerInterceptor = injector.getInstance(requestHandlerInterceptorType);
      }

      ResponseHandler<XRequest, XResponse> responseHandler;
      if (requestHandler instanceof ResponseHandler
          && requestHandler.getClass().getAnnotation(SeperateInstancesHandleRequestAndResponse.class) == null) {
        responseHandler = (ResponseHandler<XRequest, XResponse>)requestHandler;
      }
      else {
        Class<? extends ResponseHandler<XRequest, XResponse>> responseHandlerType = typeLookup.getResponseHandlerType(requestType);
        responseHandler = injector.getInstance(responseHandlerType);
        requestHandler.setRequest(request);
      }

      Class<? extends ResponseHandlerInterceptor> responseHandlerInterceptorType =
          typeLookup.getResponseHandlerInterceptorType((Class<? extends ResponseHandler<XRequest, XResponse>>)responseHandler.getClass());
      ResponseHandlerInterceptor responseHandlerInterceptor = null;
      if (responseHandlerInterceptorType != null) {
        responseHandlerInterceptor = injector.getInstance(responseHandlerInterceptorType);
      }

      return new HandlerPipelineImpl(requestHandler, requestHandlerInterceptor, responseHandler, responseHandlerInterceptor);
    }
    catch (RuntimeException e) {
      // ClassCastException, etc.
      throw e;
    }
    catch (Exception e) {
      // InstantiationException, IllegalAccessException, etc...
      throw new RuntimeException(e);
    }
  }

  private static final class HandlerPipelineImpl implements HandlerPipeline {

    private final RequestHandler<XRequest> requestHandler;
    private final RequestHandlerInterceptor requestHandlerInterceptor;

    private final ResponseHandler<XRequest, XResponse> responseHandler;
    private final ResponseHandlerInterceptor responseHandlerInterceptor;

    public HandlerPipelineImpl(RequestHandler<XRequest> requestHandler, RequestHandlerInterceptor requestHandlerInterceptor,
        ResponseHandler<XRequest, XResponse> responseHandler, ResponseHandlerInterceptor responseHandlerInterceptor) {
      this.requestHandler = requestHandler;
      this.requestHandlerInterceptor = requestHandlerInterceptor;
      this.responseHandler = responseHandler;
      this.responseHandlerInterceptor = responseHandlerInterceptor;
    }

    public RequestHandler<XRequest> getRequestHandler() {
      return requestHandler;
    }

    public RequestHandlerInterceptor getRequestHandlerInterceptor() {
      return requestHandlerInterceptor;
    }

    public ResponseHandler<XRequest, XResponse> getResponseHandler() {
      return responseHandler;
    }

    public ResponseHandlerInterceptor getResponseHandlerInterceptor() {
      return responseHandlerInterceptor;
    }
  }
}
